<?php

namespace App\Services;

use IEXBase\TronAPI\Support\Base58Check;
use IEXBase\TronAPI\Support\Utils;
use IEXBase\TronAPI\Tron;
use Illuminate\Support\Facades\Log;
use Web3\Contracts\Types\Address;
use Web3\Contracts\Types\Boolean;
use Web3\Contracts\Types\Bytes;
use Web3\Contracts\Types\DynamicBytes;
use Web3\Contracts\Types\Integer;
use Web3\Contracts\Types\Str;
use Web3\Contracts\Types\Uinteger;

class TronNode{

    protected $url;
    protected $tron;
    protected $centerAddress;
    protected $centerPassword;
    protected $collectionAddress;
    public $commission;
    public $abi;
    public function __construct(){
        $this->url = config('user.trc20_url','https://api.shasta.trongrid.io');

        $this->abi = new \Web3\Contracts\Ethabi([
            'address' => new Address,
            'bool' => new Boolean,
            'bytes' => new Bytes,
            'dynamicBytes' => new DynamicBytes,
            'int' => new Integer,
            'string' => new Str,
            'uint' => new Uinteger,
        ]);

        $this->centerAddress = '416e55c4bca719509365bd7d40bfc83a68039b5954';
        $this->centerPassword = 'e7c94e92d4c0517d8c3e7c8f3b88f0064e56364d309a09e52a1f2bcb1f462707';
        $this->collectionAddress = '41ad89466fc6545de63ce4b148d84ec0f2cf904b82';
        $this->commission = 5;
    }

    public function base58_encode($hexAddress){
        if(Utils::isHex($hexAddress)){
            return Base58Check::encode($hexAddress,0,false);
        }
        return false;
    }

    public function base58_decode($address){
        if(!Utils::isHex($address)){
            return Base58Check::decode($address,0,3);
        }
        return false;
    }

    /**
     * 创建地址
     */
    public function createAddress(){
        $method = '/wallet/generateaddress';
        $result = http_chain($this->url . $method,[]);
        Log::info('创建tron链地址：' . json_encode($result));
        if(!empty($result)){
            return $result;
        }else{
            Log::info("trc20创建地址失败");
            return false;
        }
    }

    /**
     * hexAddress目前不让通过
     * 验证地址有效性
     */
    public function validateAddress($address){
        if(Utils::isHex($address)){
            return false;
        }
        $method = '/wallet/validateaddress';
        $rpcData['address'] = $address;
        $result = http_chain($this->url . $method,$rpcData);
        if(isset($result['result'])){
            return $result['result'];
        }else{
            return false;
        }
    }

    /**
     * 提现转账
     */
    public function withdraw($toAddress,$number,$contractAddress = NULL){
        $toAddress = $this->base58_decode($toAddress);
        $ownerAddress = $this->centerAddress;
        $ownerPrivateKey = $this->centerPassword;
        return $this->sendTransaction($ownerAddress,$ownerPrivateKey,$toAddress,$number,$contractAddress);
    }

    public function collection($ownerAddress, $ownerPrivateKey, $number, $contractAddress){
        $ownerAddress = $this->base58_decode($ownerAddress);
        $toAddress = $this->collectionAddress;
        return $this->sendTransaction($ownerAddress,$ownerPrivateKey,$toAddress,$number,$contractAddress);
    }

    public function sendTransaction($ownerAddress,$ownerPrivateKey,$toAddress,$number,$contractAddress = NULL){

        try {
            $fullNode = new \IEXBase\TronAPI\Provider\HttpProvider($this->url);
            $solidityNode = new \IEXBase\TronAPI\Provider\HttpProvider($this->url);
            $eventServer = new \IEXBase\TronAPI\Provider\HttpProvider($this->url);
        } catch (\IEXBase\TronAPI\Exception\TronException $e) {
            Log::info("trc20 new失败：" . $e->getMessage());
        }

        if(!empty($contractAddress)){
            try {
                $tron = new Tron($fullNode, $solidityNode, $eventServer, null, null, $ownerPrivateKey);
                $contract = $tron->contract($contractAddress);
                $number = (float)$number;
                $result = $contract->transfer($toAddress, $number, $ownerAddress);

            } catch (\IEXBase\TronAPI\Exception\TronException $e) {
                Log::info("trc20代币转账失败：" . $e->getMessage());
                echo $e->getMessage();
            }
        }else{

            $tron = new \IEXBase\TronAPI\Tron($fullNode, $solidityNode, $eventServer);
            $tron->setAddress($ownerAddress);
            $tron->setPrivateKey($ownerPrivateKey);
            $number = (float) $number;

            try {
                $result = $tron->send($toAddress,$number);
            } catch (\IEXBase\TronAPI\Exception\TronException $e) {
                Log::info("trc20主币转账失败：" . $e->getMessage());
            }
        }
        if(isset($result['result']) && $result['result']){
            return $result['txid'];
        }
    }

    /**
     * 获取交易详情
     */
    public function getTradeInfo($tradeId){
        $method = '/wallet/gettransactionbyid';
        $rpcData['value'] = $tradeId;
        $result = http_chain($this->url . $method,$rpcData);

        if(empty($result['ret'][0]['contractRet']) || $result['ret'][0]['contractRet'] == 'FAIL' || $result['ret'][0]['contractRet'] == 'REVERT'){
            return false;
        }
        $data = [];
        if(isset($result['raw_data']['contract'][0]['parameter']['value']['contract_address'])){
            $functionDecode = substr($result['raw_data']['contract'][0]['parameter']['value']['data'],0,8);
            if($functionDecode == 'a9059cbb') {//transfer
                $input =$this->abi->decodeParameters(['address','uint256'],substr($result['raw_data']['contract'][0]['parameter']['value']['data'],8));
                //合约地址
                $data['contract_address'] = $result['raw_data']['contract'][0]['parameter']['value']['contract_address'];
                //货币数量
                $data['number'] = gmp_strval((string)$input[1]);
                //接收地址
                $data['receive_address'] = $this->base58_encode(str_replace('0x','41',$input[0]));
            }else if($functionDecode == '23b872dd'){
                $input =$this->abi->decodeParameters(['address','address','uint256'],substr($result['raw_data']['contract'][0]['parameter']['value']['data'],8));
                //合约地址
                $data['contract_address'] = $result['raw_data']['contract'][0]['parameter']['value']['contract_address'];
                //货币数量
                $data['number'] = gmp_strval((string)$input[2]);
                //接收地址
                $data['receive_address'] = $this->base58_encode(str_replace('0x','41',$input[1]));
            }
        }else{
            //合约地址
            $data['contract_address'] = '';
            //货币数量
            $data['number'] = $result['raw_data']['contract'][0]['parameter']['value']['amount'] ? $result['raw_data']['contract'][0]['parameter']['value']['amount'] : 0;
            //接收地址
            $data['receive_address'] = $this->base58_encode(str_replace('0x','41',$result['raw_data']['contract'][0]['parameter']['value']['to_address']));
        }
        if($number = $this->getBlockNumber($tradeId)){
            $data['confirmations'] = $this->getblockcount()-$number;
        }else{
            $data['confirmations'] = 0;
        }
        $data['trade_id'] = $tradeId;
        $data['send_address'] = $this->base58_encode(str_replace('0x','41',$result['raw_data']['contract'][0]['parameter']['value']['owner_address']));
        return $data;
    }

    /**
     * 获取交易所在块
     */
    public function getBlockNumber($tradeId){
        $method = '/wallet/gettransactioninfobyid';
        $rpcData['value'] = $tradeId;
        $result = http_chain($this->url . $method,$rpcData);
        return $result['blockNumber'] ? $result['blockNumber'] : false;
    }

    /**
     * 获取最新块数
     */
    public function getblockcount(){
        $method = '/wallet/getnowblock';
        $result = http_chain($this->url . $method,[]);
        if(!empty($result['block_header']['raw_data']['number'])){
            return $result['block_header']['raw_data']['number'];
        }else{
            return false;
        }
    }

    /**
     * 获取块交易
     */
    public function getBlockByNumber($number){
        $method = '/wallet/getblockbynum';
        $rpcData['num'] = $number;
        $result = http_chain($this->url . $method,$rpcData);
        if(!empty($result)){
            return $result;
        }else{
            return false;
        }
    }

    public function getBalance($address,$contractAddress = NULL){
        $address = $this->base58_decode($address);
        if(empty($contractAddress)){
            $method = '/wallet/getaccount';
            $rpcData['address'] = $address;
            $result = http_chain($this->url . $method,$rpcData);
            if(!empty($result['balance'])){
                return $result['balance'];
            }else{
                return false;
            }
        }else{
            $method = '/wallet/triggersmartcontract';
            $rpcData['contract_address'] = $contractAddress;
            $rpcData['function_selector'] = 'balanceOf(address)';
            $rpcData['owner_address'] = $address;
            $rpcData['parameter'] = substr($this->abi->encodeParameter('address',$address),2);
            $result = http_chain($this->url . $method,$rpcData);
            if($result['result']['result']){
                return gmp_strval((string)('0x' . $result['constant_result'][0]));
            }else{
                return false;
            }
        }
    }

}
